﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Shared.Method
{
    public class YcdStrTcpServer
    {
        //用于服务端
        private TcpListener _listener = null;
        private Dictionary<int, TcpClient> _dictClient = null;
        private int lastKey = 1;//最后一个键值
        private const int PACKET_SIZE = 65535;

        private int _listenerPort = 0;//监听的端口号

        private readonly object _objLock = new object();

        //事件
        public event Action<string, int> OnReceive;
        public event Action<int> OnAccept;
        public event Action<string> OnError;

        public YcdStrTcpServer(int port = 26398)
        {
            _listenerPort = port;
            _dictClient = new Dictionary<int, TcpClient>();
        }

        public void Start()
        {
            Task.Factory.StartNew(() =>
            {
                try
                {
                    _listener = new TcpListener(IPAddress.Any, _listenerPort);//监听所有IP地址

                    _listener.Start();

                    while (true)
                    {
                        var client = _listener.AcceptTcpClient();

                        _dictClient.Add(lastKey, client);

                        //传递客户端的编号
                        OnAccept?.BeginInvoke(lastKey, null, null);

                        //接收数据
                        Action<TcpClient, int> d = new Action<TcpClient, int>(ReceiveData);
                        d.BeginInvoke(client, lastKey, null, null);

                        lastKey++;

                        System.Threading.Thread.Sleep(500);
                    }

                }
                catch (Exception ex)
                {
                    OnError?.BeginInvoke(ex.Message, null, null);
                }
            });
        }

        public void Stop()
        {
            foreach (var c in _dictClient)
            {
                c.Value?.Close();
            }

            _listener?.Stop();
        }


        private void ReceiveData(TcpClient client, int key)
        {
            try
            {
                if (client == null || client.Connected == false) return;//增加方法的健壮性

                NetworkStream stream = client.GetStream();//创建读取流

                while (true)
                {
                    byte[] data = new byte[PACKET_SIZE];
                    int length = stream.Read(data, 0, PACKET_SIZE);

                    if (length == 0) { throw new Exception("接收数据长度为0"); }

                    string msg = Encoding.UTF8.GetString(data, 0, length);

                    //接收信息事件
                    OnReceive?.BeginInvoke(msg, key, null, null);
                    System.Threading.Thread.Sleep(50);
                }
            }
            catch (Exception ex)
            {
                client.Close();
                _dictClient.Remove(key);
                OnError?.BeginInvoke(ex.Message, null, null);
            }
        }

        public void SendData(int key, string msg)
        {
            //Task.Factory.StartNew(()=>
            //{
            try
            {
                TcpClient client = null;
                if (_dictClient.TryGetValue(key, out client) == false)
                {
                    throw new Exception("没有找到对应的连接，或连接失效");
                }

                if (client == null || client.Connected == false)
                {
                    throw new Exception("连接已经断开");
                }

                NetworkStream stream = client.GetStream();
                var data = Encoding.UTF8.GetBytes(msg);

                lock (_objLock)
                    stream.Write(data, 0, data.Length);
                //stream.Close();//会同样关闭SOCKET
            }

            catch (Exception ex)
            {
                _dictClient.Remove(key);
                OnError?.Invoke(ex.Message);
            }
            //});

            System.Threading.Thread.Sleep(300);
        }
    }

    public class YcdStrTcpClient
    {
        //用于用户端
        private TcpClient _client = null;
        public event Action<string> OnError;

        private const int PACKET_SIZE = 65535;

        private string _serverIp = null;//监听的IP地址
        private int _serverPort = 0;//监听的端口号

        //事件
        public event Action<string> OnReceive;

        public YcdStrTcpClient(string ip = "127.0.0.1", int port = 26398)
        {
            _serverIp = ip;
            _serverPort = port;
        }

        public void Start()
        {
            try
            {
                _client = new TcpClient(_serverIp, _serverPort);
                Action<TcpClient> d = new Action<TcpClient>(ReceiveData);
                d.BeginInvoke(_client, null, null);
            }
            catch (Exception ex)
            {
                _client?.Close();
                OnError?.BeginInvoke(ex.Message, null, null);
            }
        }

        public void Stop()
        {
            _client?.Close();
        }

        public bool ClientConnected()
        {
            if (_client == null) return false;
            return _client.Connected;
        }

        private void ReceiveData(TcpClient client)
        {
            try
            {
                if (client == null || client.Connected == false) return;

                using (NetworkStream stream = client.GetStream())//创建读取流
                {
                    while (true)
                    {
                        byte[] data = new byte[PACKET_SIZE];
                        int length = stream.Read(data, 0, PACKET_SIZE);

                        if (length == 0) { throw new Exception("接收数据长度为0"); }

                        string msg = Encoding.UTF8.GetString(data, 0, length);

                        //接收信息事件
                        OnReceive?.BeginInvoke(msg, null, null);
                        System.Threading.Thread.Sleep(50);
                    }
                }
            }
            catch (Exception ex)
            {
                client.Close();
                OnError?.BeginInvoke(ex.Message, null, null);
            }
        }

        public void SendData(string msg)
        {
            try
            {
                if (_client == null || _client.Connected == false)
                {
                    throw new Exception("未连接或连接已经断开");
                }

                NetworkStream stream = _client.GetStream();
                var data = Encoding.UTF8.GetBytes(msg);

                stream.Write(data, 0, data.Length);
            }

            catch (Exception ex)
            {
                _client?.Close();
                OnError?.Invoke(ex.Message);
            }

            System.Threading.Thread.Sleep(300);
        }
    }

    /// <summary>
    /// 基础的TCP连接类
    /// </summary>
    public class YcdBitTcpClient
    {
        //用于用户端
        private TcpClient _client = null;

        private const int PACKET_SIZE = 65535;

        private string _serverIp = null;//监听的IP地址
        private int _serverPort = 0;//监听的端口号
        private string _sourceIp = null;//本地IP地址
        private int _sourcePort = 0;//本地端口

        //事件
        public event Action<byte[], int> OnReceive;
        public event Action<string> OnError;
        public event Action<string> OnConnected;

        //初始化连接值
        public YcdBitTcpClient(string serverIp, int serverPort, string sourceIp, int sourcePort)
        {
            _serverIp = serverIp;
            _serverPort = serverPort;
            _sourceIp = sourceIp;
            _sourcePort = sourcePort;
        }

        //开始连接
        public void Start()
        {
            try
            {
                IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(_sourceIp), _sourcePort);
                _client = new TcpClient(localEP);//绑定本地EndPoint
                //_client.SendTimeout = 2000;//发送超时5秒
                _client.Connect(_serverIp, _serverPort);

                //如果连接失败，不接收数据
                if (_client.Connected == false) return;
                OnConnected?.BeginInvoke("连接成功！", null, null);

                //接收数据
                Action<TcpClient> d = new Action<TcpClient>(ReceiveData);
                d.BeginInvoke(_client, null, null);
            }
            catch (Exception ex)
            {
                _client?.Close();
                OnError?.BeginInvoke(ex.Message, null, null);
                throw new Exception(ex.ToString());
            }
        }

        public void Stop()
        {
            _client?.Close();
            _client = null;
        }

        /// <summary>
        /// 返回客户端连接情况
        /// </summary>
        /// <returns></returns>
        public bool ClientConnected()
        {
            if (_client == null) return false;
            return _client.Connected;
        }
        /// <summary>
        /// 接收数据
        /// </summary>
        /// <param name="client"></param>
        private void ReceiveData(TcpClient client)
        {
            try
            {
                if (client == null || client.Connected == false) return;

                using (NetworkStream stream = client.GetStream())//创建读取流
                {
                    while (true)
                    {
                        byte[] data = new byte[PACKET_SIZE];
                        int length = stream.Read(data, 0, PACKET_SIZE);
                        //string msg = Encoding.UTF8.GetString(data, 0, length);
                        if (length == 0)
                        {
                            throw new Exception("获取数据长度为0");
                        }
                        //接收信息事件
                        OnReceive?.BeginInvoke(data, length, null, null);
                        System.Threading.Thread.Sleep(50);
                    }
                }
            }
            catch (Exception ex)
            {
                client?.Close();
                OnError?.BeginInvoke(ex.Message, null, null);
            }
        }
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data">数据字节</param>
        /// <param name="length">数据字节长度</param>
        public void SendData(byte[] data, int length)
        {
            //Task.Factory.StartNew(()=>
            //{
            try
            {
                if (_client == null || _client.Connected == false)
                {
                    throw new Exception("连接已经断开");
                }

                NetworkStream stream = _client.GetStream();
                //var data = Encoding.UTF8.GetBytes(msg);

                stream.Write(data, 0, length);
                //下面是一个陷阱，如果关闭了client的输出流的话，client会一样关闭了
                //stream.Close();
            }

            catch (Exception ex)
            {
                _client?.Close();
                OnError?.BeginInvoke(ex.Message, null, null);
            }
            //});

            System.Threading.Thread.Sleep(200);
        }
    }

    public class StaticTcpClient
    {
        private static readonly int BUFF_SIZE = 0x200000;

        private static readonly object _lockObj = new object();

        private static Queue<YcdTcpClientGroup> s_queueTcpClient = new Queue<YcdTcpClientGroup>();

        private static YcdTcpClientGroup GetTcpClient(string ip, int port)
        {
            YcdTcpClientGroup group = null;

            if (s_queueTcpClient.Count > 0)
            {
                lock (s_queueTcpClient)
                    group = s_queueTcpClient.Dequeue();
            }
            else
            {
                group = new YcdTcpClientGroup()
                {
                    IpAddress = ip,
                    Port = port,
                    Client = new TcpClient(ip, port)
                };
            }

            //防止中途变更IP和PORT
            if (group.IpAddress != ip || group.Port != port || group.Client.Connected == false)
            {
                group = new YcdTcpClientGroup()
                {
                    IpAddress = ip,
                    Port = port,
                    Client = new TcpClient(ip, port)
                };
            }

            return group;
        }

        private static byte[] dataRecv = new byte[BUFF_SIZE];

        public static string SendAndRecvStr(string ip, int port, string sendMsg)
        {
            try
            {
                var group = GetTcpClient(ip, port);

                if (group == null || group.Client.Connected == false) return null;

                NetworkStream stream = group.Client.GetStream();

                var dataSend = Encoding.UTF8.GetBytes(sendMsg);

                if (dataSend.Length > BUFF_SIZE)
                {
                    stream.Write(dataSend, 0, BUFF_SIZE);
                }
                else
                {
                    stream.Write(dataSend, 0, dataSend.Length);
                }

                int length = stream.Read(dataRecv, 0, BUFF_SIZE);

                if (length == 0) { throw new Exception("接收数据长度为0"); }
                string msg = Encoding.UTF8.GetString(dataRecv, 0, length);

                lock (s_queueTcpClient)
                    s_queueTcpClient.Enqueue(group);

                return msg;
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrLogTxt("SendAndRecvStr", "message with background error", ex.ToString());
                throw new Exception(ex.ToString());
            }
        }

        public static string SendAndRecvStrEof(string ip, int port, string sendMsg)
        {
            try
            {
                var group = GetTcpClient(ip, port);

                if (group == null || group.Client.Connected == false) return null;

                NetworkStream stream = group.Client.GetStream();

                byte[] dataSend = Encoding.UTF8.GetBytes(sendMsg);

                #region 使用0xFF标识一个数据的结束

                if (dataSend.Length > BUFF_SIZE)//大于缓存的数据不发送
                {
                    return null;
                }

                byte[] dataSendEof = new byte[dataSend.Length + 1];
                Buffer.BlockCopy(dataSend, 0, dataSendEof, 0, dataSend.Length);
                dataSendEof[dataSend.Length] = 0xFF;
                stream.Write(dataSendEof, 0, dataSendEof.Length);

                #endregion
#if false
                if (dataSend.Length > BUFF_SIZE)
                {
                    stream.Write(dataSend, 0, BUFF_SIZE);
                }
                else
                {
                    stream.Write(dataSend, 0, dataSend.Length);
                }

                int length = stream.Read(dataRecv, 0, BUFF_SIZE);

                if (length == 0) { throw new Exception("接收数据长度为0"); }
                string msg = Encoding.UTF8.GetString(dataRecv, 0, length);
#endif
                int offset = 0;//接收缓存的开始位置
                int length = 0;//接收缓存长度

                stream.ReadTimeout = 2000;//2S

                while (
                    (length = stream.Read(dataRecv, offset, BUFF_SIZE - offset)) > 0)
                {
                    if (dataRecv[length + offset-1] != 0xFF)
                    {
                        offset += length;
                    }
                    else
                    {
                        break;
                    }
                }

                if (dataRecv[length + offset-1] != 0xFF) return null;

                string msg = Encoding.UTF8.GetString(dataRecv, 0, offset + length - 1);

                lock (s_queueTcpClient)
                    s_queueTcpClient.Enqueue(group);

                return msg;
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrLogTxt("SendAndRecvStrEof", "message with background error", ex.ToString());
                throw new Exception(ex.ToString());
            }
        }


        public static void CloseAll()
        {
            while (s_queueTcpClient.Count > 0)
            {
                var group = s_queueTcpClient.Dequeue();
                group.Client.Close();
            }
        }
    }
    /// <summary>
    /// tcp连接类结构体
    /// </summary>
    public class YcdTcpClientGroup
    {
        public string IpAddress { get; set; }
        public int Port { get; set; }
        public TcpClient Client { get; set; }
    }
}
